http://blog.sina.com.cn/s/blog_a01c59f101015tjd.html
八数码问题
这是课程中的一个实验,问题比较有趣,所有代码都是本人自行设计编写,所以特意写出来供大家学习和参考,做到这个实验的同学千万不要完整复制我写的代码哦,参考学习一下,你可以自己写出更好的代码。
问题描述:
给定九宫格的初始状态,要求在有限步的操作内,使其转化为目标状态,且所得到的解是代价最小解(即移动的步数最少)。例如:
2
1
7
求解思路:
用启发式搜索算法求解,A*算法。
首先定义了六个结构体:一是表示九宫格的各个数字,二是表示OPEN结点,三是表示OPEN表,四是表示CLOSED结点,五是表示CLOSED表,六是表示求解路径。自定义的函数有:初始化九宫格;判断两个九宫格是否一致;求某结点的hx值,即与目标结点九宫格不一样的元素个数;对OPEN表按照估价函数值的大小从小到大排序;判断某九宫格是否与OPEN表中某结点相同;判断某九宫格是否与CLOSED表中某结点相同;将一个九宫格中的数字全部复制到另一个九宫格中;求OPEN表中某结点的扩展结点;删除OPEN表中第一个结点;计算估价函数值并赋值;以矩阵形式打印九宫格;打印求解最终路径;启发式搜索函数寻找求解路径。
算法描述:
procedure
End
求解代码:
#include<iostream.h>
#include<malloc.h>
#define
#define
#define
//自定义结构体
typedef
int
}Squared;
typedef
Squared
int
int
int
}Open;
typedef
Open
Open
}OpenSqua;
typedef
int
Squared
}Closed;
typedef
Closed
}ClosedSqua;
typedef
Open
}Path;
//自定义结构体
//初始化九宫格
void
for(int
for(int
s.num[i][j]=0;
}
}
}
//初始化九宫格
//判断两个九宫格是否一致
bool
for(int
for(int
if(a.num[i][j]!=b.num[i][j])
return
}
}
return
}
//判断两个九宫格是否一致
//求某结点的hx值,即与目标结点九宫格不一样的元素个数
int
int
for(int
for(int
if(a.num[i][j]!=end.num[i][j])
count++;
}
}
return
}
//求某结点的hx值,即与目标结点九宫格不一样的元素个数
//对OPEN表按照估价函数值的大小从小到大排序
void
int
for(i=0;i<n-1;i++){
int
for(j=i+1;j<n;j++){
if(op.squanote[j].fx<op.squanote[index].fx)
index=j;
if(index!=i){
Open
op.squanote[i]=op.squanote[index];
op.squanote[index]=temp;
}
}
}
}
//对OPEN表按照估价函数值的大小从小到大排序
//判断某九宫格是否与OPEN表中某结点相同
bool
for(int
if(EqualSqua(op.squanote[i].stateself,s))
return
}
return
}
//判断某九宫格是否与OPEN表中某结点相同
//判断某九宫格是否与CLOSED表中某结点相同
bool
for(int
if(EqualSqua(close.squanote[i].stateself,s))
return
}
return
}
//判断某九宫格是否与CLOSED表中某结点相同
//将一个九宫格中的数字全部复制到另一个九宫格中
void
for(int
for(int
a.num[i][j]=b.num[i][j];
}
}
}
//将一个九宫格中的数字全部复制到另一个九宫格中
//求OPEN表中某结点的扩展结点
bool
int
for(i=0;i<3;i++){
for(j=0;j<3;j++){
if(note.stateself.num[i][j]==0){
if(i-1>=0){
CopySqua(s[k].stateself,note.stateself);
s[k].stateself.num[i][j]=note.stateself.num[i-1][j];
s[k].stateself.num[i-1][j]=note.stateself.num[i][j];
s[k].dx=1;
k++;
}
if(j+1<=2){
CopySqua(s[k].stateself,note.stateself);
s[k].stateself.num[i][j]=s[k].stateself.num[i][j+1];
s[k].stateself.num[i][j+1]=0;
s[k].dx=1;
k++;
}
if(i+1<=2){
CopySqua(s[k].stateself,note.stateself);
s[k].stateself.num[i][j]=s[k].stateself.num[i+1][j];
s[k].stateself.num[i+1][j]=0;
s[k].dx=1;
k++;
}
if(j-1>=0){
CopySqua(s[k].stateself,note.stateself);
s[k].stateself.num[i][j]=s[k].stateself.num[i][j-1];
s[k].stateself.num[i][j-1]=0;
s[k].dx=1;
k++;
}
return
}
}
}
return
}
//求OPEN表中某结点的扩展结点
//删除OPEN表中第一个结点
void
int
for(i=0;i<cnt;i++){
a.squanote[i]=a.squanote[i+1];
}
}
//删除OPEN表中第一个结点
//计算估价函数值并赋值
void
op.dx=pare.dx+1;
op.hx=GetHx(op.stateself,end);
op.fx=op.dx+op.hx;
}
//计算估价函数值并赋值
//以矩阵形式打印九宫格
void
for(int
cout<<"\t";
for(int
if(s.num[i][j]==0)
cout<<"
else
cout<<s.num[i][j]<<"
}
cout<<"\n";
}
}
//以矩阵形式打印九宫格
//打印求解最终路径
void
int
Open
endPath=new
for(i=smp;i>0;i--){
i=smp;
for(j=i-1;j>=0;j--){
if(GetHx(path.pa[j].stateself,path.pa[i].stateself)==2){
endPath[count++]=path.pa[i];
break;
}
}
smp=j;
}
endPath[count++]=path.pa[0];
for(i=count-1;i>=0;i--){
PrintSqua(endPath[i].stateself);
cout<<"\n";
}
}
//打印求解最终路径
//启发式搜索函数寻找求解路径
bool
int
int
OpenSqua
ClosedSqua
Path
Open
open.squanote[cntop].stateself=start;
open.squanote[cntop].dx=0;
open.squanote[cntop].hx=GetHx(open.squanote[cntop].stateself,end);
open.squanote[cntop].fx=open.squanote[cntop].dx+open.squanote[cntop].hx;
cntop++;
while(cntop!=0){
nop=open.squanote[0];
path.pa[cntpa++]=nop;
DeleteOne(open,cntop);
cntop--;
if(EqualSqua(nop.stateself,end)){
ShowPath(path,cntpa);
return
}
Open
CreateSub(sub,nop);
if(!CreateSub(sub,nop))
continue;
for(int
if(sub[i].dx==1){
if(!ExistOpen(open,sub[i].stateself)&&!ExistClosed(close,sub[i].stateself)){
CalEvaluate(sub[i],nop,end);
open.squanote[cntop]=sub[i];
open.squapare[cntop]=nop;
cntop++;
continue;
}
if(ExistOpen(open,sub[i].stateself)){
CalEvaluate(sub[i],nop,end);
if(sub[i].fx<nop.fx){
open.squanote[cntop]=sub[i];
open.squapare[cntop]=nop;
cntop++;
continue;
}
}
if(ExistClosed(close,sub[i].stateself)){
CalEvaluate(sub[i],nop,end);
if(sub[i].fx<close.squanote[0].num){
open.squanote[cntop]=sub[i];
open.squapare[cntop]=nop;
cntop++;
continue;
}
}
}
}
close.squanote[cntcl].stateself=nop.stateself;
close.squanote[cntcl].num=nop.fx;
cntcl++;
SortOpen(open,cntop);
if(count++>300){
break;
}
}
return
}
//启发式搜索函数寻找求解路径
//在主函数中测试
void
cout<<"========================九宫重排问题========================\n";
cout<<"问题描述:\n\t给定九宫格的初始状态,要求在有限步的操作内,使其转化为\n";
cout<<"
cout<<"
cout<<"则表示你所要表示的九宫格为:\n";
cout<<"\t1
cout<<"========================输入始末状态========================\n";
Squared
int
InitSqua(inits);
InitSqua(goals);
cout<<"请输入九宫的初始状态:(数字为空时用0替代)\n";
for(i=0;i<3;i++){
for(j=0;j<3;j++){
cin>>x;
inits.num[i][j]=x;
}
}
cout<<"您输入的初始状态为:\n";
PrintSqua(inits);
cout<<"请输入九宫的目标状态:(数字为空时用0替代)\n";
for(i=0;i<3;i++){
for(j=0;j<3;j++){
cin>>x;
goals.num[i][j]=x;
}
}
cout<<"您输入的目标状态为:\n";
PrintSqua(goals);
cout<<"========================打印求解路径========================\n";
cout<<"求解路径为:\n";
if(Heuristic_Search(inits,goals)){
cout<<"success!\n";
}else{
cout<<"no
}
cout<<"========================问题演示结束========================\n";
}
//在主函数中测试
心得体会:
本题程序的编写基本上就是花了一个比较长的整时间进行的编写。刚开始没有一点头绪,想想每种策略都会有很多分支,不知如何取舍,在仔细阅读书本中有关A*算法的描述后,结合实验指导书提供的算法伪代码,基本上确立了整体的框架,在进入主题之前,我首先解决的是九宫格的输入输出问题,使得不仅方便输入,而且输出的九宫格也很形象。结合算法中所需要的操作,又编写了复制九宫格、计算代价、排序、判断某九宫格是否在某一表中、求扩展结点等辅助函数。最后在核心算法函数中进行调用,根据A*算法,编写出了搜索函数,在测试过程中,每次都从最简单的情况开始测试,寻找错误的地方,一步一步进行纠正,最终将书上的案例数据都测试通过了,不过本程序有一些缺陷,我使用了定长顺序表来存放选择步骤,所以这个对于简单的案例会造成占用了较多的空间,却没有用上,而对于复杂的案例,也许顺序表不够长,最终无法得出正确结果。考虑到本题没有判断某数据是否有解的函数,所以只能一步一步搜索,为防止无止境的搜索,我仍然没有改变定长顺序表,这样当搜索进行一定步骤之后,如果还是无解,我就认为这是无解的。最后,同样对界面进行了优化。
这个程序最大的缺陷就是,我也不知道是否能解所有问题,比较简单的测试数据可以得到正确解,而有的测试数据,也不知道是否真的无解。这个问题比较纠结了。个人感觉结构体设计的不太好,浪费空间资源,数据有些冗余,有待改进。
谢谢!